﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Runtime.Remoting;
using System.Text;
using Microsoft.Win32;
using System.Data.SqlClient;

using System.Data;
using BusinessLayer = Microsoft.Clm.BusinessLayer;
using Microsoft.Clm.Common;
using Microsoft.Clm.Common.TypedData;
using Microsoft.Clm.Common.Requests;
using Microsoft.Clm.Provision;
using ProvisionApiRequests = Microsoft.Clm.Shared.Requests;
using Microsoft.Clm.Shared.ProfileTemplates;
using Microsoft.Clm.Shared.Profiles;


namespace FimCmExtensions
{
    public class FimCmProxy : MarshalByRefObject
    {
        // TraceSource for the FIM CM Extension
        private static TraceSourceEx trace = TraceSourceEx.Instance;  

        /// <summary>
        /// Check that the user given by userUuid has Read and Enroll permissions on the
        /// profile template if requestType is Enroll or TempCardEnroll
        /// </summary>
        /// <param name="requestType"></param>
        /// <param name="userUuid"></param>
        /// <param name="profileTemplate"></param>
        public Boolean CheckEnrollProfileTemplateRights(RequestType requestType, Guid userUuid, Guid profileTemplateUuid)
        {
            //Microsoft.Clm.Shared.ProfileTemplates.ProfileTemplate profileTemplate = Microsoft.Clm.BusinessLayer.Shared.FindOperations.GetProfileTemplate(profileTemplateUuid);

            Microsoft.Clm.Common.AD.UserProfile profileTemplate = Microsoft.Clm.BusinessLayer.UserProfiles.GetProfileTemplate(profileTemplateUuid);
                //Microsoft.Clm.BusinessLayer.Profiles.GetProfileTemplate(profileTemplateUuid);
            //if (requestType != RequestType.Enroll && requestType != RequestType.TemporaryCardEnroll)
                //return;

            string targetUserName = BusinessLayer.Users.ConvertGuidToNT4(userUuid);
            try
            {
                BusinessLayer.Security.UserProfileAccess.CheckUserProfileAccessAndThrow(targetUserName,
                    profileTemplate,
                    UserProfileRights.Enroll | UserProfileRights.Read);
            }
            catch (UnauthorizedAccessException)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Check that the user given by logonName has permissions on the profile template
        /// </summary>
        /// <param name="targetUserName"></param>
        /// <param name="profileTemplateName"></param>        
        public Boolean CheckEnrollProfileTemplateRights(String targetUserName, String profileTemplateName)
        {
     
            /// Get the Profile by ProfileTemplateName
            Microsoft.Clm.Common.AD.UserProfile profileTemplateMatch = GetUserProfile(profileTemplateName);
   
            try
            {
                ///
                /// Check the Enroll and Read Access
                /// 
                BusinessLayer.Security.UserProfileAccess.CheckUserProfileAccessAndThrow(targetUserName,
                    profileTemplateMatch,
                    UserProfileRights.Enroll | UserProfileRights.Read);
            }
            catch (UnauthorizedAccessException)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Get a ProfileTemplate by Name
        /// </summary>
        /// <param name="profileTemplateName">the ProfileTemplate Name</param>
        /// <returns>UserProfile object containing the ProfileTemplate</returns>
        internal Microsoft.Clm.Common.AD.UserProfile GetUserProfile(String profileTemplateName)
        {
            // Get the Profile by ProfileTemplateName
            Microsoft.Clm.Common.AD.UserProfile profileTemplateMatch = null;
            Microsoft.Clm.Common.AD.UserProfile[] allProfiles = Microsoft.Clm.BusinessLayer.UserProfiles.GetAllUserProfiles();
            foreach (Microsoft.Clm.Common.AD.UserProfile profileTemplate in allProfiles)
            {
                if (profileTemplate.DisplayName.Equals(profileTemplateName, StringComparison.InvariantCultureIgnoreCase))
                {
                    profileTemplateMatch = profileTemplate;
                    break; //we found the match, get outta here
                }
            }

            if (profileTemplateMatch == null)
            {
                throw new ArgumentException(String.Format("ProfileTemplate with display name of '{0}' does not exist.", profileTemplateName));
            }

            return profileTemplateMatch;
        }

        /// <summary>
        /// Get the domain\logonname of the current user.  Useful for troubleshooting .NET Remoting issues
        /// </summary>
        /// <returns></returns>
        public String CurrentUserName()
        {
            return Microsoft.Clm.BusinessLayer.Security.CurrentUserName;
        }

        /// <summary>
        /// Gets the Request Secrets (one time passwords) for a given request
        /// </summary>
        /// <param name="requestUUID"></param>
        /// <returns>String Array Containing the Request Secrets</returns>
        public String[] GetRequestSecrets(Guid requestUUID)
        {
            // Load the Request from the UUID
            Microsoft.Clm.Common.Requests.Request request = Microsoft.Clm.BusinessLayer.Requests.GetRequest(requestUUID);

            // Get the Request Secrets
            Microsoft.Clm.Common.Requests.SecretDistributionResults secrets = Microsoft.Clm.BusinessLayer.Requests.DistributeSecrets(request);

            // Copy the Request Secrets into a String Array
            string[] requestSecrets = new string[secrets.Count];
            for (int i = 0; i < secrets.Count; i++)
            {
                requestSecrets[i] = secrets[i].SecretValue.ToString();
            }

            return requestSecrets;
        }

        /// <summary>
        /// Get the Request object from CM
        /// </summary>
        /// <remarks>This is a simple wrapper for the Provision API call</remarks>
        /// <param name="requestUUID">the CM request GUID</param>
        /// <returns>a Request object</returns>
        public ProvisionApiRequests.Request GetRequestByGuid(Guid requestUuid)
        {
            // Turn off remoting since we are calling from inside CM
            FindOperations.UseRemoting = false;

            // return the request
            return FindOperations.GetRequest(requestUuid);
        }

        /// <summary>
        /// Get the Profile Templates
        /// </summary>
        /// <remarks>This is a simple wrapper for the Provision API call</remarks>
        /// <returns>ProfileTemples</returns>
        public ReadOnlyCollection<ProfileTemplate> FindAllProfileTemplates()
        {
            // Turn off remoting since we are calling from inside CM
            FindOperations.UseRemoting = false;

            // return the ProfileTemplates
            return FindOperations.FindAllProfileTemplates();
        }

        /// <summary>
        /// Get the Profile
        /// </summary>
        /// <remarks>This is a simple wrapper for the Provision API call</remarks>
        /// <returns>Profile</returns>
        public Profile GetProfile(Guid profileUuid)
        {
            // Turn off remoting since we are calling from inside CM
            FindOperations.UseRemoting = false;

            // return the Profile
            return FindOperations.GetProfile(profileUuid);
        }

        /// <summary>
        /// Initiate a CM Enroll Request
        /// </summary>
        /// <param name="targetUserName"></param>
        /// <param name="profileTemplateName"></param>
        /// <returns>GUID of the new Request</returns>
        public Guid InitiateEnrollRequest(String targetUserName, String profileTemplateName)
        {
            // TODO: Validate the targetUserName            
            // TODO: Allow the caller to supply the dataCollection items
            // TODO: Allow the caller to supply the comments
            // TODO: Allow the caller to supply the request priority
            trace.TraceStart("InitiateEnrollRequest");
            trace.TraceInfo("Hello FIM CM Proxy Trace");

            // Create a dummy dataCollection
            // Even though we don't support dataCollection right now, we can't create a request with a null dataCollection
            List<ProvisionApiRequests.DataCollectionItem> dataCollectionList = new List<ProvisionApiRequests.DataCollectionItem>();
            dataCollectionList.Add(new ProvisionApiRequests.DataCollectionItem("FOO", "bar"));
            ProvisionApiRequests.DataCollection dataCollection = new ProvisionApiRequests.DataCollection(dataCollectionList);

            String comments = "FOO";
            Byte requestPriority = 1;

            // Resolve the ProfileTemplateName
            Microsoft.Clm.Common.AD.UserProfile profileTemplateMatch = GetUserProfile(profileTemplateName);
            Guid profileTemplateGuid = profileTemplateMatch.Uuid;

            // Get the TargetUser GUID
            Guid targetUserGuid = Microsoft.Clm.BusinessLayer.Users.ConvertNameToGuid(targetUserName);

            // Get the Requestor GUID
            Guid requestorGuid = Microsoft.Clm.BusinessLayer.Users.ConvertNameToGuid(this.CurrentUserName());

            //Guid requestGuid = this.InitiateEnrollRequestByProfileUuid(profileTemplateGuid, requestorGuid, targetUserGuid, dataCollection, comments, requestPriority);
            RequestOperations.UseRemoting = false;
            Microsoft.Clm.Shared.Requests.Request request = RequestOperations.InitiateEnroll(profileTemplateGuid, dataCollection, targetUserGuid, comments, requestPriority);

            trace.TraceStop("InitiateEnrollRequest");
            return request.Uuid;
        }

        ///
        /// Suspend 
        /// 
        public ProvisionApiRequests.Request SuspendProfile(Guid profileUuid, String comment)
        {
            Byte requestPriority = 1;
            HybridDictionary dataCollection = new HybridDictionary();

            // Create the Request using the BusinessLayer
            Request request = BusinessLayer.Create.CreateSuspendRequestByProfileUuid(profileUuid, comment, requestPriority);
            
            ValidatedData validatedData = BusinessLayer.DataValidator.Validate(Microsoft.Clm.Common.ApplicationUserRole.CertificateManager, request, dataCollection);

            RequestState requestState = BusinessLayer.Submit.SubmitRequest(ref request, validatedData);

            //return requestState.RequestUUID;

            ///
            /// Suspend
            /// 
            FindOperations.UseRemoting = false;
            Profile profile = FindOperations.GetProfile(request.ProfileUuid);

            RevokeParameters revokeParms = new RevokeParameters(Microsoft.Clm.Common.Requests.RevocationReason.CertificateHold, new System.TimeSpan(5000));
      
            //Microsoft.Clm.BusinessLayer.RequestExecution.RevokeProfile22(request.RequestUUID, revokeParms, "SuspendOrReinstate Comment");
            Microsoft.Clm.BusinessLayer.RequestExecution.RevokeProfile(request.RequestUUID, revokeParms, "Suspended");

            ///
            /// Complete the Request
            /// 
            ExecuteOperations.UseRemoting = false;
            return ExecuteOperations.Complete(request.RequestUUID);
        }

        ///
        /// Reinstate 
        /// 
        public ProvisionApiRequests.Request ReinstateProfile(Guid profileUuid, String comment)
        {
            Byte requestPriority = 1;
            HybridDictionary dataCollection = new HybridDictionary();

            // Create the Request using the BusinessLayer
            Request request = BusinessLayer.Create.CreateReinstateRequestByProfileUuid(profileUuid, comment, requestPriority);

            ValidatedData validatedData = BusinessLayer.DataValidator.Validate(Microsoft.Clm.Common.ApplicationUserRole.CertificateManager, request, dataCollection);

            RequestState requestState = BusinessLayer.Submit.SubmitRequest(ref request, validatedData);

            //return requestState.RequestUUID;

            FindOperations.UseRemoting = false;
            Profile profile = FindOperations.GetProfile(request.ProfileUuid);

            RevokeParameters revokeParms = new RevokeParameters(Microsoft.Clm.Common.Requests.RevocationReason.CertificateReinstate, new System.TimeSpan(5000));
            
            Microsoft.Clm.BusinessLayer.RequestExecution.RevokeProfile(request.RequestUUID, revokeParms, "Reuinted");

            ///
            /// Complete the Request
            /// 
            ExecuteOperations.UseRemoting = false;
            return ExecuteOperations.Complete(request.RequestUUID);
        }


        /// <summary>
        /// Return the ConnectionString for the CLM Database
        /// </summary>
        /// <returns>a Connection String for the CLM database</returns>
        private String GetConnectionStringFromClm()
        {
            const string keyName = @"SOFTWARE\Microsoft\Clm\v1.0\Setup";
            const string keyValue = @"WebFolder";
            string configFile = null;

            RegistryKey key = null;
            try
            {
                key = Registry.LocalMachine.OpenSubKey(keyName);
                string path = (string)key.GetValue(keyValue);

                if (!path.EndsWith(@"\"))
                    path += @"\";
                configFile = path + "web.config";
            }
            finally
            {
                if (key != null)
                {
                    key.Close();
                }
            }

            ClmConfiguration.LoadFromFile(configFile);

            string dbConnectionString = ClmConfiguration.ConnectionString;

            return dbConnectionString;
        }

        public DataTable FullImportProfiles()
        {
            ///
            /// Connect to SQL
            /// 
            SqlConnection connection = new SqlConnection();
            connection.ConnectionString = this.GetConnectionStringFromClm();
            connection.Open();

            string QueryString = "select Profiles.* , UserNameCache.unc_user_nt4_name AS pr_assigned_user_name, sc_status, sc_serial_number, sc_permanent_sc_uuid";
            QueryString += " FROM Profiles with (NOLOCK)";
            QueryString += " LEFT OUTER JOIN UserNameCache ON Profiles.pr_assigned_user_uuid = UserNameCache.unc_user_uuid";
            QueryString += " LEFT OUTER JOIN Smartcards ON Profiles.pr_sc_uuid = Smartcards.sc_uuid";

            SqlDataAdapter da = new SqlDataAdapter(QueryString, connection);
            System.Data.DataSet ds = new System.Data.DataSet();
            da.Fill(ds, "FullImportProfiles");

            connection.Close();

            return ds.Tables["FullImportProfiles"];
        }

        public DataTable FullImportRequests()
        {
            ///
            /// Connect to SQL
            /// 
            SqlConnection connection = new SqlConnection();
            connection.ConnectionString = this.GetConnectionStringFromClm();
            connection.Open();

            Int32 requestStatusCompleted = (int)Microsoft.Clm.Shared.Requests.RequestStatus.Completed;
            Int32 requestStatusDenied = (int)Microsoft.Clm.Shared.Requests.RequestStatus.Denied;
            Int32 requestStatusFailed = (int)Microsoft.Clm.Shared.Requests.RequestStatus.Failed;
            Int32 requestStatusCanceled = (int)Microsoft.Clm.Shared.Requests.RequestStatus.Canceled;

            // Filter out all completed, denied, failed and canceled requests on full import only.
            // We still need them durring delta import so that we can turn them into a delete.
            string QueryString = "SELECT Requests.* , UserNameCache.unc_user_nt4_name AS req_target_user_name ";
            QueryString += "FROM Requests WITH (NOLOCK) LEFT OUTER JOIN UserNameCache ON Requests.req_target_user_uuid = UserNameCache.unc_user_uuid ";
            QueryString += "WHERE req_status <> '" + requestStatusCompleted + "' ";
            QueryString += "AND req_status <> '" + requestStatusDenied + "' ";
            QueryString += "AND req_status <> '" + requestStatusFailed + "' ";
            QueryString += "AND req_status <> '" + requestStatusCanceled + "' ";

            SqlDataAdapter da = new SqlDataAdapter(QueryString, connection);

            System.Data.DataSet ds = new System.Data.DataSet();
            da.Fill(ds, "FullImportRequests");

            connection.Close();

            return ds.Tables["FullImportRequests"];
        }
    }
}
